#pragma once

#include <iostream>
#include <cstring>
#include <string>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>

#include <signal.h>

#include "log.hpp"
#include "Task.hpp"
#include "ThreadPool.hpp"

static const std::string defaultIp = "127.0.0.1";  // 反正推荐的是bind任意的ip

namespace Server{

    enum{
        USAGE_ERROR = 1,
        SOCKET_ERROR,
        BIND_ERROR,
        LISTEN_ERROR
    };

    static const uint16_t gport = 880;
    static const int gbacklog = 5;  // 后面会有这个参数的解释
    // 声明一下，因为下面的 ThreadData 类(传递给线程的数据的类)需要用到下面的tcpServer类
    class tcpServer;  

    // 传递给线程的数据
    class ThreadData{
    public:
        ThreadData(tcpServer* self, int sock)
            : _self(self), _sock(sock)
        {}

    public:
        tcpServer* _self;
        int _sock;
    };

    class tcpServer{
    public:
        tcpServer(const uint16_t& port, const std::string& ip = defaultIp)
            : _port(port), _ip(ip), _sock(-1) {}

        void initServer(){
            // 1. 
            _sock = socket(AF_INET, SOCK_STREAM, 0);
            if(_sock < 0){
                logMessage(FATAL, "create socket error");
                exit(SOCKET_ERROR);
            }
            logMessage(NORMAL, "create socket success");

            // 2. 
            struct sockaddr_in local;
            bzero(&local, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = inet_addr(_ip.c_str());
            if(bind(_sock, (struct sockaddr*)&local, sizeof(local)) < 0){
                logMessage(FATAL, "bind socket error");
                exit(BIND_ERROR);
            }
            logMessage(NORMAL, "bind socket success");

            // 3. 
            // 第二个参数后面再理解
            if(listen(_sock, gbacklog) < 0){
                logMessage(FATAL, "listen socket error");
                exit(LISTEN_ERROR);
            }
            logMessage(NORMAL, "listen socket success");
        }

        void start(){
            // 4. 线程池初始化
            ThreadPool<Task>::getInstance()->run();
            logMessage(NORMAL, "Thread init success");

            // version 2.1 多进程操作的版本(忽略异常信号的方式达到多进程的目的)
            // signal(SIGCHLD, SIG_IGN);
            while(true){
                // 4. 获取新数据

                struct sockaddr_in peer;
                bzero(&peer, sizeof(peer));
                socklen_t len = sizeof(peer);  // len 这个变量好像是一个输入型变量，也好像是一个输出型变量
                // accept 函数的返回值是 client 端的 sock，我们是用这个 sock来通信的
                // 因为套接字socket具有全双工的特性
                int sock = accept(_sock, (struct sockaddr*)&peer, &len);
                if(sock < 0){
                    logMessage(DEBUG, "accept error, next");
                    continue;
                }

                logMessage(NORMAL, "accept a new link success, get new sock: %d", sock); // ?
                logMessage(DEBUG, "accept error, next");
                logMessage(WARNING, "accept error, next");
                logMessage(FATAL, "accept error, next");
                logMessage(NORMAL, "accept error, next");

                logMessage(NORMAL, "accept a new link success, get new sock: %d", sock); // ?
                logMessage(DEBUG, "accept error, next");
                logMessage(WARNING, "accept error, next");
                logMessage(FATAL, "accept error, next");
                logMessage(NORMAL, "accept error, next");

                logMessage(NORMAL, "accept a new link success, get new sock: %d", sock); // ?
                logMessage(DEBUG, "accept error, next");
                logMessage(WARNING, "accept error, next");
                logMessage(FATAL, "accept error, next");
                logMessage(NORMAL, "accept error, next");

                // 5. 这里就是一个 sock，未来通信我们就用这个sock，面向字节流的，后续全部都是文件操作
                
                // version 1 文件操作版
                // serviceIO(sock);  // 传递一个通信用的 sock
                // version 1 是一个单进程版本的，只有上面的serviceIO(sock) 函数里面的死循环退出了(client 关闭了它的 sock)
                // 那么我们 server 端也关闭 sock
                // 再者说，对一个已经使用完毕的sock，我们要关闭这个sock，要不然会有文件符泄漏的问题
                // close(sock);  


                // version 2.2 多进程操作的版本(创建孙子进程达到多进程的目的)
                // pid_t id = fork();
                // if(id == 0){  // child
                //     // 子进程继承父进程的sock，与父进程的sock是相互独立额的
                //     // 而且子进程也用不上继承下来的sock，不释放还会有问题。就close了
                //     close(_sock); 

                //     // 开一个新的子进程(孙子进程，让孙子进程去处理任务。孙子进程就变成了孤儿进程了，被init进程收养)
                //     // 子进程退出，不让父进程阻塞式等待 --> 多进程的现象
                //     if(fork() > 0) exit(0);

                //     serviceIO(sock);
                //     close(sock);
                //     exit(0);
                // }
                // // father
                // pid_t ret = waitpid(id, nullptr, 0);
                // if(ret > 0)
                //     std::cout << "wait success: " << ret << std::endl;


                // version 3 多线程操作的版本(创建子线程，子线程分离。达到多线程的目的)
                // pthread_t tid;
                // ThreadData* td = new ThreadData(this, sock);
                // // 因为 threadRoutine 函数是一个 static 类型的函数，没有 this 指针，所以用不了 this 对象里面的成员
                // // 而且 threadRoutine 要使用的还有通信要用的 sock，所以就创建了一个 threadData 类来传递数据
                // pthread_create(&tid, nullptr, threadRoutine, td);
                // // pthread_join(tid, nullptr);


                // version 4 多线程操作的版本(提前开辟好的线程池，达到多线程的目的)
                ThreadPool<Task>::getInstance()->push(Task(sock, serviceIO));
            }
        }

        // version 3 多线程操作的版本
        // (version 4 是不用下面的函数的，因为下面的函数已经被写入到 Task.hpp 头文件里面去了。如果要用的话，那么 serviceIO 函数就相对于是一个全局的函数了)
        // static void * threadRoutine(void* args){
        //     // 体格线程不能同时是detached 和 joinable 的。
        //     // 设置为线程分离状态，这样主线程就不用阻塞式 join 了，就可以达到多线程的目的了。
        //     pthread_detach(pthread_self());
        //     // static_cast 是一个安全的类型转化的用法
        //     ThreadData* td = static_cast<ThreadData*>(args);

        //     // td 类里面的 _self 是 tcpServer类 类型的哦。是可以调用 serviceIO() 函数的哦 
        //     td->_self->serviceIO(td->_sock);

        //     close(td->_sock);
        //     delete td;
        //     return nullptr;
        // }

        // 下面的这个函数写到 Task.hpp 头文件里面去了，因为这个函数并没有用到当前类的什么成员变量的哦
        // version 1 单线程 文件操作的版本
        // version 2 多进程操作的版本
        // void serviceIO(int sock){
        //     char buffer[NUM];
        //     while(true){
        //         // 1. 读取 sock(类似管道) 里面的数据
        //         // read 函数的返回值是 ssize_t 类型的
        //         int n = read(sock, buffer, sizeof(buffer) - 1);  // 留一个位置给 \0
        //         if(n > 0) {
        //             // 目前我们把读到的数据当成字符串, 截止目前
        //             buffer[n] = 0;
                    
        //             std::cout << "<tcpServer.hpp> DEBUG: " << buffer << std::endl;

        //             // 都读取出来的数据信息做对应的处理 ...

        //             // 2. 把处理完成的信息再发送回去
        //             std::string message = buffer;   // c 和 cpp 的字符串是可以相互隐式类型转化的
        //             message += " server[echo]";

        //             // write 函数的返回值这里先不做处理，后面会有地方处理的
        //             write(sock, message.c_str(), message.size());
        //         }else if(n == 0){
        //             // 代表 client 退出了
        //             logMessage(NORMAL, "client quit, me too");
        //             break;
        //         }
        //     }

        //     close(sock);
        // }

        ~tcpServer(){}

    private:
        int _sock;
        uint16_t _port;
        std::string _ip;
    };
}  // namespace server

